package tools.NW3D;

public abstract class DynamicProgramming {

	protected String sequence1;
	protected String sequence2;
	protected Cell[][] scoreTable;
	protected boolean tableIsFilledIn;
	protected boolean isInitialized;
	
	
	
	public DynamicProgramming(String sequence1, String sequence2) {
		super();
		this.sequence1 = sequence1;
		this.sequence2 = sequence2;
		scoreTable = new Cell[sequence1.length()+1][sequence2.length()+1];
	}
	
	public int[][] getScoreTable(){
		ensureTableIsFilledIn();
		
		int[][] matrix= new int[scoreTable.length][scoreTable[0].length];
		
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				matrix[i][j] = scoreTable[i][j].getScore();
			}
		}
		
		return matrix;
	}
	
	protected void initialize() {
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				scoreTable[i][j] = new Cell(i,j);
			}
		}
		initializeScores();
		initializePointers();
		initializeKmers();
	
		isInitialized = true;	
	}
	
	protected void initializeScores(){
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				scoreTable[i][j].setScore(getInitialScore(i,j));
			}
		}
	}
	
	protected void initializePointers(){
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				scoreTable[i][j].setPrevCell(getInitialPointer(i,j));
			}
		}
	}
	
	protected void initializeKmers(){
		for(int i=0;i<scoreTable.length;++i){
			for(int j=0;j<scoreTable[i].length;++j){
				scoreTable[i][j].setKmer(getInitialKmer(i,j));
			}
		}
	}
	
	protected abstract String getInitialKmer(int row, int col);
	
	protected abstract int getInitialScore(int row, int col);
	
	protected abstract Cell getInitialPointer(int row, int col);
	
	protected abstract void fillInCell(Cell currentCell,Cell cellAbove, Cell cellToLeft, Cell cellAboveLeft);
	
	protected void fillIn(){
		for(int i=1;i<scoreTable.length;++i){
			for(int j=1;j<scoreTable[i].length;++j){
				Cell currentCell= scoreTable[i][j];
				Cell cellAbove= scoreTable[i-1][j];
				Cell cellToLeft= scoreTable[i][j-1];
				Cell cellAboveLeft= scoreTable[i-1][j-1];
				fillInCell(currentCell, cellAbove, cellToLeft, cellAboveLeft);
			}
		}
		
		tableIsFilledIn=true;
	}
	
	abstract protected Object getTraceback();
	
	protected void ensureTableIsFilledIn(){
		if(!isInitialized){
			initialize();
		}
		if(!tableIsFilledIn){
			fillIn();
		}
	}
}
